<!--
 * @Author: lmh
 * @Date: 2021-09-11 22:53:22
 * @LastEditTime: 2021-09-11 22:59:59
 * @LastEditors: lmh
 * @Description: 处理数组响应化问题
-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        let ARRY_METHOD = [
            'push', 'pop', 'shift', 'unshift', 'reverse', 'sort', 'splice'
        ]
        let array_methods = Object.create(Array.prototype)

        ARRY_METHOD.forEach(method => {
            array_methods[method] = function() {
                // 调用原来的方法
                console.log('调用的是拦截的 ' + method + ' 方法')

                // 将数据进行响应式化
                for(let i = 0; i < arguments.length; i++) {
                    reactify(arguments[i])
                }
                let res = Array.prototype[method].apply(this, arguments)
                return res
            }
        })

        // 简化后版本
        function defineReactive(target, key, value, enumerable) {
            // 函数内部是一个局部作用域， 这个value就只在函数内部使用的变量（闭包）

            if (typeof value === 'object' && value != null && !Array.isArray(value)) {
                // 非数组引用类型
                reactify(value) // 递归
            }

            Object.defineProperty(target, key, {
                configurable: true,
                enumerable: !!enumerable,
                get () {
                    console.log(`读取 o 的 ${key} 属性`)
                    return value
                },
                set (newVal) {
                    console.log(`设置 o 的 ${key} 属性为：${newVal}`)
                    value = newVal
                }
            })
        }

        // 将对象进行响应式化
        function reactify(o) {
            let keys = Object.keys(o)

            for(let i = 0; i < keys.length; i++) {
                let key = keys[i] // 属性名
                let value = o[key]
                if ( Array.isArray(value) ) {
                    // 数组
                    value.__proto__ = array_methods // 数组就响应式了
                    for(let j = 0; j < value.length; j++) {
                        reactify(value[j]) // 递归
                    }
                } else {
                    // 对象或者值类型
                    defineReactive(o, key, value, true)
                }
            }
        }

        let data = {
            name: '章三',
            age: 19,
            course: [
                {
                    name: '语文'
                },
                {
                    name: '数学'
                },
                {
                    name: '英语'
                }
            ]
        }
        reactify(data)
    </script>
</body>
</html>
